﻿using System.Collections.Generic;
using System.Linq;

namespace ComeSeeThis_MVC2.Models
{
    public class GearingRecRepository
    {
        private readonly ComeSeeThisDBEntities _entities = new ComeSeeThisDBEntities();

        #region Query Methods

        #region Gearing Recs

        public IQueryable<GearingRec> GetAllGearingRecs()
        {
            return _entities.GearingRecs.OrderBy(x => x.VehicleType.VehicleTypeName).OrderBy(x => x.Motor.MotorName).OrderBy(x => x.Motor.Company.CompanyName);
        }

        public IQueryable<GearingRec> GetAllGearingRecsByMotorCompanyId(int companyId)
        {
            return _entities.GearingRecs.Where(c => c.Motor.CompanyId == companyId);
        }

        public IQueryable<GearingRec> GetAllGearingRecsByMotorId(int motorId)
        {
            return _entities.GearingRecs.Where(c => c.Motor.MotorId == motorId);
        }

        public IQueryable<GearingRec> GetAllGearingRecsByVehicleId(int vehicleId)
        {
            return _entities.GearingRecs.Where(c => c.VehicleId == vehicleId);
        }

        public IQueryable<GearingRec> GetAllGearingRecsByVehicleTypeId(int vehicleTypeId)
        {
            return _entities.GearingRecs.Where(vt => vt.VehicleTypeId == vehicleTypeId);
        }

        public IQueryable<GearingRec> GetAllGearingRecsByMotorTurns(float turns)
        {
            return _entities.GearingRecs.Where(t => t.Motor.Turns == turns);
        }

        public GearingRec GetGearingRec(int gearingRecId)
        {
            return _entities.GearingRecs.SingleOrDefault(g => g.GearingRecId == gearingRecId);
        }

        public IQueryable<GearingRec> SearchGearingRecs(GearingRec gearingRec)
        {
            // Get the basic matches, all gearing recs for the selected vehicle type
            var motor = new MotorRepository().GetMotor(gearingRec.MotorId);

            // Exact matches
            var exactMatchList =
                _entities.GearingRecs.Where(x => x.VehicleId == gearingRec.VehicleId).Where(
                    x => x.VehicleTypeId == gearingRec.VehicleTypeId).Where(x => x.MotorId == gearingRec.MotorId).Where(
                        x => x.CellCount == gearingRec.CellCount).Where(x => x.CellTypeId == gearingRec.CellTypeId).
                    Where(x => x.TrackSizeId == gearingRec.TrackSizeId).Where(
                        x => x.TrackSurfaceId == gearingRec.TrackSurfaceId).Where(
                            x => x.EscTimingAdvance == gearingRec.EscTimingAdvance);

            if (exactMatchList.Count() >= 5) return exactMatchList;

            //var list1 = _entities.GearingRecs.Where(x => x.VehicleId == gearingRec.VehicleId).Where(
            //        x => x.VehicleTypeId == gearingRec.VehicleTypeId).Where(x => x.MotorId == gearingRec.MotorId).Where(
            //            x => x.CellCount == gearingRec.CellCount).Where(x => x.CellTypeId == gearingRec.CellTypeId).
            //        Where(x => x.TrackSizeId != gearingRec.TrackSizeId).Where(
            //            x => x.TrackSurfaceId != gearingRec.TrackSurfaceId).Where(
            //                x => x.EscTimingAdvance == gearingRec.EscTimingAdvance);

            // So we have less than 5 matches...
            // Look for matches disregarding track size & surface
            var list1 = from g in _entities.GearingRecs
                        where g.VehicleId == gearingRec.VehicleId && g.VehicleTypeId == gearingRec.VehicleTypeId
                             && g.CellCount == gearingRec.CellCount && g.CellTypeId == gearingRec.CellTypeId
                             && g.MotorId == gearingRec.MotorId && (g.TrackSizeId != gearingRec.TrackSizeId || g.TrackSurfaceId != gearingRec.TrackSurfaceId)
                             && !exactMatchList.Contains(g)
                        //&& g.EscTimingAdvance == gearingRec.EscTimingAdvance
                        select g;

            var result = exactMatchList.Concat(list1);
            //if (result.Count() >= 5) return result;

            // Shit, still less than 5 results...
            // Try again disregarding exact vehicle
            var list2 = from g in _entities.GearingRecs
                        where g.VehicleId != gearingRec.VehicleId && g.VehicleTypeId == gearingRec.VehicleTypeId
                             && g.CellCount == gearingRec.CellCount && g.CellTypeId == gearingRec.CellTypeId
                             && g.MotorId == gearingRec.MotorId && (g.TrackSizeId != gearingRec.TrackSizeId || g.TrackSurfaceId != gearingRec.TrackSurfaceId)
                             && !list1.Contains(g)
                             && !exactMatchList.Contains(g)
                        //&& g.EscTimingAdvance == gearingRec.EscTimingAdvance
                        select g;

            result = result.Concat(list2);
            //if (result.Count() >= 5) return result;

            //var list2B = from g in _entities.GearingRecs
            //             where g.VehicleId != gearingRec.VehicleId && g.VehicleTypeId == gearingRec.VehicleTypeId
            //                 && g.CellCount == gearingRec.CellCount && g.CellTypeId == gearingRec.CellTypeId
            //                 && g.MotorId == gearingRec.MotorId && (g.TrackSizeId != gearingRec.TrackSizeId || g.TrackSurfaceId != gearingRec.TrackSurfaceId)
            //                 //&& g.EscTimingAdvance == gearingRec.EscTimingAdvance
            //            select g;

            //result = result.Concat(list2B);
            //if (result.Count() >= 5) return result;

            // Ok, still not even 5 matches
            // This time try any motor that is within 1000 KiloVolt 
            if (motor.KiloVolt != null)
            {
                var maxKv = (int)motor.KiloVolt + 1000;
                var minKv = (int)motor.KiloVolt - 1000;

                var list3 = from g in _entities.GearingRecs
                            where g.VehicleTypeId == gearingRec.VehicleTypeId
                                  && g.CellCount == gearingRec.CellCount && g.CellTypeId == gearingRec.CellTypeId
                                  && (g.Motor.KiloVolt >= minKv && g.Motor.KiloVolt <= maxKv)
                                //&& g.EscTimingAdvance == gearingRec.EscTimingAdvance
                             && !list1.Contains(g)
                             && !list2.Contains(g)
                             && !exactMatchList.Contains(g)
                            select g;
                result = result.Concat(list3);
                //if (result.Count() >= 5) return result;
            }

            // Seriously, like WTF?? we still don't have 5 entries????
            // Ok, let's expand the turns within 1
            if (motor.Turns != null)
            {
                var maxTurns = (int)motor.Turns + 1;
                var minTurns = (int)motor.Turns - 1;

                var list3 = from g in _entities.GearingRecs
                            where g.VehicleTypeId == gearingRec.VehicleTypeId
                                  && g.CellCount == gearingRec.CellCount && g.CellTypeId == gearingRec.CellTypeId
                                  && (g.Motor.Turns >= minTurns && g.Motor.Turns <= maxTurns)
                                  && !list1.Contains(g)
                                  && !list2.Contains(g)
                                  && !exactMatchList.Contains(g)
                            //&& g.EscTimingAdvance == gearingRec.EscTimingAdvance
                            select g;
                result = result.Concat(list3);
                //if (result.Count() >= 5) return result;
            }

            //var firstquery = from permissionids in Page_Permissions
            //                 where permissionids.Page_ID = 1
            //                 select permissionids.Permission_ID;
            //var secondquery = from pers in Permissions where !firstquery.Contains(pers.Permission_ID)  ;

            // Still not 5 results, fuckit, just return what we have
            return result;

            var masterList = _entities.GearingRecs.Where(x => x.VehicleTypeId == gearingRec.VehicleTypeId).Where(x => x.CellTypeId == gearingRec.CellTypeId).Where(x => x.CellCount == gearingRec.CellCount);

            // Only filter by motor if user didn't select "Any" motor
            if (motor.MotorId > 1) masterList = masterList.Where(x => x.Motor.Turns == motor.Turns);

            var matches1 = masterList.Where(x => x.VehicleId == gearingRec.VehicleId);


            // Let's deal with some wild cards - If user selected a specific Vehicle, Motor, Track Size or Track Surface
            //if (gearingRec.VehicleId > 1)  matches = matches.Where(x => x.VehicleId == gearingRec.VehicleId);
            if (gearingRec.MotorId > 1) matches1 = matches1.Where(x => x.MotorId == gearingRec.MotorId);
            if (gearingRec.TrackSizeId > 1) matches1 = matches1.Where(x => x.TrackSizeId == gearingRec.TrackSizeId);
            if (gearingRec.TrackSurfaceId > 1) matches1 = matches1.Where(x => x.TrackSurfaceId == gearingRec.TrackSurfaceId);

            // Now the standard ones that are always selected
            matches1 = matches1.Where(x => x.CellTypeId == gearingRec.CellTypeId).Where(x => x.CellCount == gearingRec.CellCount);

            // Get Similar matches
            // Same vehicle type, different vehicle or no vehicle, all motors
            var matches2 = masterList.Where(x => x.VehicleId == null || x.VehicleId != gearingRec.VehicleId); // Where(x => x.Motor.Turns == motor.Turns).
            // Same vehicle, different motor
            var matches3 = masterList.Where(x => x.VehicleId == gearingRec.VehicleId && x.MotorId != gearingRec.MotorId);
            IQueryable<GearingRec> matches4 = null;// = new IQueryable<GearingRec>();
            if (matches1.Count() == 0)
            {
                // Shitty, no results were found :(
                // Same vehicle type, not the same vehicle, any motor
                matches4 = _entities.GearingRecs.Where(x => x.VehicleTypeId == gearingRec.VehicleTypeId);
            }

            return matches1.Concat(matches2).Concat(matches3);//.Concat(matches4);
        }

        #endregion

        #region Cell Types

        public IQueryable<CellType> GetAllCellTypes()
        {
            return _entities.CellTypes;
        }

        public CellType GetCellType(int cellTypeId)
        {
            return _entities.CellTypes.SingleOrDefault(ct => ct.CellTypeId == cellTypeId);
        }

        #endregion

        #region TrackSize

        public IQueryable<TrackSize> GetAllTrackSizes()
        {
            return _entities.TrackSizes;
        }

        public TrackSize GetTrackSize(int trackSizeId)
        {
            return _entities.TrackSizes.SingleOrDefault(ct => ct.TrackSizeId == trackSizeId);
        }

        #endregion

        #region TrackSurface

        public IQueryable<TrackSurface> GetAllTrackSurfaces()
        {
            return _entities.TrackSurfaces;
        }

        public TrackSurface GetTrackSurface(int trackSurfaceId)
        {
            return _entities.TrackSurfaces.SingleOrDefault(ct => ct.TrackSurfaceId == trackSurfaceId);
        }

        #endregion


        #endregion

        #region Insert / Delete Methods

        public void Add(GearingRec gearingRec)
        {
            _entities.GearingRecs.AddObject(gearingRec);
        }

        public void Delete(GearingRec gearingRec)
        {
            _entities.GearingRecs.DeleteObject(gearingRec);
        }

        #endregion

        #region Persistance

        public void Save()
        {
            _entities.SaveChanges();
        }

        #endregion
    }
}